package cppcloud

import (
	"encoding/json"
	"fmt"
	"math/rand"
	"reflect"
	"sync"
	"time"
)

const (
	QueueLen = 20
)

type MessageData struct {
	Cmdid uint16
	Seqid uint16
	Body  interface{}
}

type MessageHandle interface {
	ProcessMessage(errCode int, cmdid, seqid uint16, bodystr string) *MessageData
}

type TCPClient struct {
	*TCPCli

	sndQ       chan *MessageData
	rcvQ       chan MessageData
	rcvMapping *handleMapType

	seqid        uint16
	exitfg       bool
	waitGroup    sync.WaitGroup
	routineCount int
	reqTOSec     int // 请求超时值-秒
}

// MakeTCPClient 创建TCP客户端实例
func MakeTCPClient(svrAddr string, attrMap map[string]interface{}, timoSec int) (tcli *TCPClient) {
	rand.Seed(time.Now().UnixNano())
	cli := MakeTCPCli(svrAddr, attrMap, timoSec)
	if nil == cli {
		return nil
	}

	tcli = &TCPClient{}
	tcli.TCPCli = cli
	tcli.sndQ = make(chan *MessageData, QueueLen)
	tcli.rcvMapping = createHandleMap()
	tcli.reqTOSec = timoSec

	return tcli
}

// SetCMDHandler 设置一个消息处理回调
// param: seqid 为0时匹配所有，即仅由cmdid决定消息处理
// param: hander 为nil时执行移除
func (tcli *TCPClient) SetCMDHandler(cmdid, seqid uint16, hander *MessageHandle, once bool) {
	tcli.rcvMapping.set((uint32(cmdid)<<16)+uint32(seqid), hander, once)
}

// Start 客户端启动运行， 接收线程和发送线程在后台工作
func (tcli *TCPClient) Start() int {
	if !tcli.checkConnect() {
		return -10
	}

	tcli.exitfg = false
	tcli.waitGroup.Add(2)
	tcli.routineCount = 2
	go tcli.recvRoutine()
	go tcli.sendRoutine()
	return 0
}

func (tcli *TCPClient) Join() {
	tcli.waitGroup.Wait()
}

func (tcli *TCPClient) Shutdown() {
	tcli.exitfg = true
	tcli.invokeCallBack("shutdown", nil)
	tcli.sndQ <- &MessageData{CMD_KEEPALIVE_REQ, 0, ""} // 目的是令接收线程跳出block等待
	close(tcli.sndQ)
}

func (tcli *TCPClient) routineFinishCall(msg string) {
	tcli.waitGroup.Done()
	tcli.routineCount--
	fin := ""
	if tcli.routineCount < 1 {
		tcli.close()
		fin = "done"
	}
	fmt.Println(msg, "exit", fin)
}

// 接收协程
func (tcli *TCPClient) recvRoutine() {
	defer tcli.routineFinishCall("recvRoutine")

	for !tcli.exitfg {
		if cmdid, seqid, bodystr, err := tcli.recv(); nil == err {
			fmt.Printf("TCPRECV| 0x%x-%d len=%d\n", cmdid, seqid, len(bodystr))
			//tcli.rcvQ <- MessageData{cmdid, seqid, bodystr}
			key0 := (uint32(cmdid) << 16) + uint32(seqid)
			key1 := (uint32(cmdid) << 16)
			keys := [2]uint32{key0, key1}
			hasHandle := false

			for _, key := range keys {
				if hander, once := tcli.rcvMapping.get(key); nil != hander {
					hasHandle = true
					rsp := (*hander).ProcessMessage(0, cmdid, seqid, bodystr)
					if nil != rsp { // 处理完后有消息回复
						tcli.sndQ <- rsp
					}

					if once {
						tcli.rcvMapping.set(key, nil, false) // delete handle
					}
					break
				}
			}

			if !hasHandle {
				fmt.Printf("UNHANDLE-MSG| 0x%X-%d %s %v\n", cmdid, seqid, bodystr, err)
			}

		} else {
			fmt.Println("RECV-ERR| ", err)
		}
	}
}

// 发送协程
func (tcli *TCPClient) sendRoutine() {
	defer tcli.routineFinishCall("sendRoutine")

	for !tcli.exitfg {
		msg, ok := <-tcli.sndQ
		if !ok {
			break
		}

		var nSnd int
		if strBody, okStr := msg.Body.(string); okStr {
			nSnd = tcli.sendString(msg.Cmdid, msg.Seqid, strBody)
		} else if strByte, okByt := msg.Body.([]byte); okByt {
			nSnd = tcli.sendByteArr(msg.Cmdid, msg.Seqid, strByte)
		} else if strMap, okMap := msg.Body.(map[string]interface{}); okMap {
			nSnd = tcli.sendMap(msg.Cmdid, msg.Seqid, strMap)
		} else if list, okList := msg.Body.([]interface{}); okList {
			byteData, err := json.Marshal(list)
			if nil == err {
				nSnd = tcli.sendByteArr(msg.Cmdid, msg.Seqid, byteData)
			}
		} else {
			fmt.Println("INVALID-BYDY-TYPE|", reflect.TypeOf(msg.Body), msg.Body)
		}

		if nSnd < 1 {
			fmt.Println("SND-ERROR|", nSnd, "msg=", msg)
		}
	}
}

type syncWaitType struct {
	qch     chan *MessageData
	errCode int
	tmoSec  int // 请求超时时间
}

func (sw *syncWaitType) ProcessMessage(errCode int, cmdid, seqid uint16, bodystr string) *MessageData {
	sw.errCode = errCode

	select {
	case sw.qch <- &MessageData{cmdid, seqid, bodystr}:
		// 成功写入
	default:
	}
	return nil
}
func (sw *syncWaitType) waitResponse() (msg string, errCode int) {

	select {
	case rsp, ok := <-sw.qch:
		errCode = sw.errCode
		if ok && nil != rsp {
			msg = rsp.Body.(string)
		} else {
			msg = "response body null"
			errCode = -4
		}
	case <-time.After(time.Duration(sw.tmoSec) * time.Second):
		return "response timeout", -2
	}

	return
}

// Request 同步请求
func (tcli *TCPClient) RequestMap(cmdid uint16, msg interface{}) (ret map[string]interface{}, errCode int) {
	rspStr, ecode := tcli.Request(cmdid, msg)
	errCode = ecode
	if nil == ret {
		ret = make(map[string]interface{})
	}
	json.Unmarshal([]byte(rspStr), &ret)
	return
}
func (tcli *TCPClient) Request(cmdid uint16, msg interface{}) (ret string, errCode int) {
	if tcli.exitfg {
		return "", -1
	}
	tcli.seqid++
	seqid := tcli.seqid
	rspCmdid := (cmdid | CMDID_MID)

	syncWaiter := syncWaitType{qch: make(chan *MessageData), tmoSec: tcli.reqTOSec}
	var interf MessageHandle = &syncWaiter
	tcli.SetCMDHandler(rspCmdid, seqid, &interf, true)
	defer close(syncWaiter.qch)
	defer tcli.SetCMDHandler(rspCmdid, seqid, nil, true)
	tcli.sndQ <- &MessageData{cmdid, seqid, msg}

	var rspMsg string
	if rspMsg, errCode = syncWaiter.waitResponse(); 0 == errCode {
		ret = rspMsg
		//ret = make(map[string]interface{})
		//json.Unmarshal([]byte(rspMsg), &ret)
	}

	return
}

func (tcli *TCPClient) RequestNoWait(cmdid uint16, msg interface{}) {
	tcli.seqid++
	seqid := tcli.seqid
	tcli.sndQ <- &MessageData{cmdid, seqid, msg}
}

/////////////////////////////////////////////////
